package com.gxar.marketdelivery.domain.factory;

import com.google.common.collect.Lists;
import com.gxar.marketdelivery.domain.aviator.enums.PeriodType;
import com.gxar.marketdelivery.domain.enums.DeliveryResSlotStatus;
import com.gxar.marketdelivery.domain.enums.DeliveryStatus;
import com.gxar.marketdelivery.domain.enums.DeliveryTarget;
import com.gxar.marketdelivery.domain.repo.DeliveryMaterialRepository;
import com.gxar.marketdelivery.domain.repo.DeliveryPlanRepository;
import com.gxar.marketdelivery.domain.repo.DeliveryResSlotRepository;
import com.gxar.marketdelivery.domain.repo.DeliveryRuleRepository;
import com.gxar.marketdelivery.infra.dao.entity.DeliveryMaterialEntity;
import com.gxar.marketdelivery.infra.dao.entity.DeliveryPlanEntity;
import com.gxar.marketdelivery.infra.dao.entity.DeliveryResSlotEntity;
import com.gxar.marketdelivery.infra.dao.entity.DeliveryRuleEntity;
import com.gxar.marketdelivery.interfaces.assembler.DeliveryMaterialConverter;
import com.gxar.marketdelivery.interfaces.assembler.DeliveryPlanConverter;
import com.gxar.marketdelivery.interfaces.web.result.IResponseStatusMsg;
import com.gxar.marketdelivery.shared.model.DeliveryMaterialModel;
import com.gxar.marketdelivery.shared.model.DeliveryPlanModel;
import com.gxar.marketdelivery.shared.model.DeliveryResSlotModel;
import com.gxar.marketdelivery.shared.model.DeliveryRuleModel;
import com.gxar.marketdelivery.shared.model.OpInfo;
import com.gxar.marketdelivery.shared.utils.AssemblerUtils2;
import com.gxar.marketdelivery.shared.utils.AssertUtils;
import com.gxar.marketdelivery.shared.utils.CollectionUtils;
import com.gxar.marketdelivery.shared.utils.DateTimeUtils;
import com.gxar.marketdelivery.shared.utils.ExtractUtils;
import com.gxar.marketdelivery.shared.utils.JacksonUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author nice
 * @version 1.0
 * @created 2023/7/20.
 */
@Service
public class DeliveryFactory {

    @Autowired
    protected DeliveryResSlotRepository deliveryResSlotRepository;
    @Autowired
    protected DeliveryMaterialRepository deliveryMaterialRepository;
    @Autowired
    protected DeliveryPlanRepository deliveryPlanRepository;
    @Autowired
    protected DeliveryRuleRepository deliveryRuleRepository;

    /**
     * 新建资源位
     *
     * @param resSlotModel
     * @param opInfo
     * @return
     */
    public DeliveryResSlotModel createResSlot(DeliveryResSlotModel resSlotModel, OpInfo opInfo) {
        DeliveryResSlotEntity entity = new DeliveryResSlotEntity();
        AssemblerUtils2.assemble(resSlotModel, entity);
        entity.setModifier(opInfo.getOpId());
        entity.setCreator(opInfo.getOpId());
        entity.setResSlotStatus(DeliveryResSlotStatus.OPEN.getCode());
        deliveryResSlotRepository.create(entity);
        DeliveryResSlotModel returnModel = new DeliveryResSlotModel();
        AssemblerUtils2.assemble(resSlotModel, returnModel);
        returnModel.setResSlotStatus(entity.getResSlotStatus());
        DeliveryMaterialModel materialModel = createMaterial(resSlotModel.getMaterialModel(), DeliveryTarget.RES_SLOT,
                entity.getId());
        returnModel.setMaterialModel(materialModel);
        returnModel.setId(entity.getId());
        return returnModel;
    }

    private DeliveryMaterialModel createMaterial(DeliveryMaterialModel materialModel, DeliveryTarget deliveryTarget,
            Long targetId) {
        if (materialModel == null) {
            return null;
        }
        DeliveryMaterialEntity materialEntity = new DeliveryMaterialEntity();
        AssemblerUtils2.assemble(materialModel, materialEntity);
        materialEntity.setDeliveryTarget(deliveryTarget.getCode());
        materialEntity.setTargetId(targetId);
        materialEntity.setId(null);
        deliveryMaterialRepository.create(materialEntity);
        return DeliveryMaterialConverter.convertToModel(materialEntity);
    }

    /**
     * 更新资源位
     *
     * @param resSlotModel
     * @param oldResSlotEntity
     * @param opInfo
     */
    public DeliveryResSlotModel updateResSlot(DeliveryResSlotModel resSlotModel, DeliveryResSlotEntity oldResSlotEntity,
            OpInfo opInfo) {
        DeliveryResSlotEntity entity = new DeliveryResSlotEntity();
        AssemblerUtils2.assemble(resSlotModel, entity);
        entity.setId(oldResSlotEntity.getId());
        entity.setModifier(opInfo.getOpId());
        entity.setResSlotStatus(null);
        deliveryResSlotRepository.updateNotNull(entity);
        deliveryMaterialRepository.delete(DeliveryTarget.RES_SLOT, entity.getId());
        DeliveryResSlotModel returnModel = new DeliveryResSlotModel();
        AssemblerUtils2.assemble(resSlotModel, returnModel);
        DeliveryMaterialModel materialModel = createMaterial(resSlotModel.getMaterialModel(), DeliveryTarget.RES_SLOT,
                entity.getId());
        returnModel.setMaterialModel(materialModel);
        returnModel.setId(entity.getId());
        return returnModel;
    }

    public DeliveryResSlotModel getResSlot(Long resSlotId) {
        DeliveryResSlotEntity entity = deliveryResSlotRepository.getById(resSlotId);
        if (entity == null) {
            return null;
        }
        DeliveryResSlotModel resSlotModel = new DeliveryResSlotModel();
        AssemblerUtils2.assemble(entity, resSlotModel);
        DeliveryMaterialEntity materialEntity = deliveryMaterialRepository.getByTarget(DeliveryTarget.RES_SLOT,
                entity.getId());
        resSlotModel.setMaterialModel(DeliveryMaterialConverter.convertToModel(materialEntity));
        return resSlotModel;
    }

    public DeliveryResSlotModel getResSlotByResSlotKey(String resSlotKey) {
        DeliveryResSlotEntity entity = deliveryResSlotRepository.getByKey(resSlotKey);
        if (entity == null) {
            return null;
        }
        DeliveryResSlotModel resSlotModel = new DeliveryResSlotModel();
        AssemblerUtils2.assemble(entity, resSlotModel);
        DeliveryMaterialEntity materialEntity = deliveryMaterialRepository.getByTarget(DeliveryTarget.RES_SLOT,
                entity.getId());
        resSlotModel.setMaterialModel(DeliveryMaterialConverter.convertToModel(materialEntity));
        List<DeliveryPlanEntity> planEntities = deliveryPlanRepository.getByResSlotId(entity.getId());
        resSlotModel.setPlanModels(convertToModelAndFill(planEntities));
        return resSlotModel;
    }

    public List<DeliveryResSlotModel> getResSlotByResSlotKeys(List<String> resSlotKeys) {
        List<DeliveryResSlotEntity> resSlotEntities = deliveryResSlotRepository.getByKeys(resSlotKeys);
        if (CollectionUtils.isEmpty(resSlotEntities)) {
            return Collections.emptyList();
        }
        List<DeliveryResSlotModel> result = AssemblerUtils2.assembleList2NewList(resSlotEntities,
                DeliveryResSlotModel.class);
        for (DeliveryResSlotModel resSlotModel : result) {
            DeliveryMaterialEntity materialEntity = deliveryMaterialRepository.getByTarget(DeliveryTarget.RES_SLOT,
                    resSlotModel.getId());
            resSlotModel.setMaterialModel(DeliveryMaterialConverter.convertToModel(materialEntity));
            List<DeliveryPlanEntity> planEntities = deliveryPlanRepository.getByResSlotId(resSlotModel.getId());
            resSlotModel.setPlanModels(convertToModelAndFill(planEntities));
        }
        return result;
    }

    ///////////////// 投放计划相关 ////////////////////

    /**
     * 创建投放计划
     *
     * @param planModel
     * @return
     */
    public DeliveryPlanModel createDeliveryPlan(DeliveryPlanModel planModel) {
        DeliveryPlanEntity planEntity = new DeliveryPlanEntity();
        AssemblerUtils2.assemble(planModel, planEntity);
        planEntity.setPlanStatus(DeliveryStatus.WAIT.getCode());
        planEntity.setCreator(OpInfo.SYS_OP.getOpId());
        planEntity.setModifier(OpInfo.SYS_OP.getOpId());
        deliveryPlanRepository.create(planEntity);
        DeliveryPlanModel returnPlanModel = AssemblerUtils2.assemble(planEntity, DeliveryPlanModel.class);
        DeliveryMaterialModel returnMaterialModel = createMaterial(planModel.getMaterialModel(), DeliveryTarget.PLAN,
                planEntity.getId());
        returnPlanModel.setMaterialModel(returnMaterialModel);
        DeliveryRuleModel returnRuleModel = createDeliveryRule(planModel.getDeliveryRuleModel(), DeliveryTarget.PLAN,
                planEntity.getId());
        returnPlanModel.setDeliveryRuleModel(returnRuleModel);
        return returnPlanModel;
    }

    /**
     * 更新投放计划
     *
     * @param planModel
     * @param oldPlanEntity
     * @return
     */
    public DeliveryPlanModel updateDeliveryPlan(DeliveryPlanModel planModel, DeliveryPlanEntity oldPlanEntity) {
        Long planId;
        DeliveryPlanEntity planEntity = new DeliveryPlanEntity();
        AssemblerUtils2.assemble(planModel, planEntity);
        if (oldPlanEntity == null) {
            planEntity.setPlanStatus(DeliveryStatus.WAIT.getCode());
            planEntity.setCreator(OpInfo.SYS_OP.getOpId());
            deliveryPlanRepository.create(planEntity);
            planId = planEntity.getId();
        } else {
            planEntity.setId(oldPlanEntity.getId());
            planEntity.setPlanStatus(oldPlanEntity.getPlanStatus());
            deliveryPlanRepository.updateNotNull(planEntity);
            planId = oldPlanEntity.getId();
            //删除旧物料
            deliveryMaterialRepository.delete(DeliveryTarget.PLAN, planId);
            DeliveryRuleEntity ruleEntity = deliveryRuleRepository.getByTargetId(DeliveryTarget.PLAN, planId);
            if (ruleEntity != null) {
                deliveryRuleRepository.deleteByTargetId(DeliveryTarget.PLAN, planId);
            }
        }

        DeliveryPlanModel returnPlanModel = AssemblerUtils2.assemble(planEntity, DeliveryPlanModel.class);

        DeliveryMaterialModel returnMaterialModel = createMaterial(planModel.getMaterialModel(), DeliveryTarget.PLAN,
                planId);
        returnPlanModel.setMaterialModel(returnMaterialModel);
        DeliveryRuleModel returnRuleModel = createDeliveryRule(planModel.getDeliveryRuleModel(), DeliveryTarget.PLAN,
                planId);
        returnPlanModel.setDeliveryRuleModel(returnRuleModel);
        return returnPlanModel;
    }

    private DeliveryRuleModel createDeliveryRule(DeliveryRuleModel rule, DeliveryTarget deliveryTarget, Long targetId) {
        DeliveryRuleEntity ruleEntity = new DeliveryRuleEntity();
        AssemblerUtils2.assemble(rule, ruleEntity);
        ruleEntity.setShowWithoutLogin(ObjectUtils.defaultIfNull(rule.getShowWithoutLogin(), Boolean.FALSE));
        ruleEntity.setDeliveryTarget(deliveryTarget.getCode());
        ruleEntity.setTargetId(targetId);
        if (rule.getStartTime() != null) {
            ruleEntity.setStartTime(DateTimeUtils.dateStart(new Date(rule.getStartTime())));
        }
        if (rule.getEndTime() != null) {
            ruleEntity.setEndTime(DateTimeUtils.dateEnd(new Date(rule.getEndTime())));
        }
        if (rule.getPeriodModels() == null) {
            rule.setPeriodModels(Collections.emptyList());
        }
        if (!PeriodType.ONLINE_ALL.equalsByCode(rule.getPeriodType())) {
            ruleEntity.setPeriodInfo(JacksonUtils.toJsonString(rule.getPeriodModels(), false));
        }
        deliveryRuleRepository.create(ruleEntity);
        return DeliveryPlanConverter.convertToRuleModel(ruleEntity);
    }

    public void deletePlan(DeliveryPlanEntity oldPlanEntity) {
        Long planId = oldPlanEntity.getId();
        deliveryPlanRepository.deletePlan(planId, OpInfo.SYS_OP.getOpId());
        deliveryRuleRepository.deleteByTargetId(DeliveryTarget.PLAN, planId);
        deliveryMaterialRepository.delete(DeliveryTarget.PLAN, planId);
    }

    public DeliveryPlanModel getDeliveryPlan(Long planId) {
        DeliveryPlanEntity entity = deliveryPlanRepository.getById(planId);
        if (entity == null) {
            return null;
        }
        DeliveryRuleEntity ruleEntity = deliveryRuleRepository.getByTargetId(DeliveryTarget.PLAN, entity.getId());
        AssertUtils.notNull(ruleEntity, IResponseStatusMsg.APIEnum.PARAM_ERROR, "投放规则不存在");
        Long ruleId = ruleEntity.getId();
        DeliveryPlanModel planModel = new DeliveryPlanModel();
        AssemblerUtils2.assemble(entity, planModel);
        planModel.setDeliveryRuleModel(DeliveryPlanConverter.convertToRuleModel(ruleEntity));
        DeliveryMaterialEntity materialEntity = deliveryMaterialRepository.getByTarget(DeliveryTarget.PLAN,
                entity.getId());
        planModel.setMaterialModel(DeliveryMaterialConverter.convertToModel(materialEntity));
        return planModel;
    }

    public List<DeliveryPlanModel> convertToModelAndFill(List<DeliveryPlanEntity> planEntities) {
        if (CollectionUtils.isEmpty(planEntities)) {
            return Collections.emptyList();
        }
        List<Long> planIds = ExtractUtils.extractField(planEntities, DeliveryPlanEntity::getId);
        Map<Long, DeliveryRuleEntity> ruleMap = deliveryRuleRepository.multiGet(DeliveryTarget.PLAN, planIds);
        List<Long> ruleIds = ruleMap.values().stream().map(DeliveryRuleEntity::getId).collect(Collectors.toList());
        Map<Long, List<DeliveryMaterialEntity>> materialMap = deliveryMaterialRepository.multiGet(DeliveryTarget.PLAN,
                planIds);
        List<DeliveryPlanModel> planModels = Lists.newArrayList();
        planEntities.forEach(plan -> {
            DeliveryPlanModel planModel = new DeliveryPlanModel();
            AssemblerUtils2.assemble(plan, planModel);
            Long planId = plan.getId();
            List<DeliveryMaterialEntity> materialEntities = materialMap.get(planId);
            planModel.setMaterialModel(
                    DeliveryMaterialConverter.convertToModel(CollectionUtils.getFirst(materialEntities)));
            DeliveryRuleEntity ruleEntity = ruleMap.get(planId);
            if (ruleEntity == null) {
                return;
            }
            DeliveryRuleModel ruleModel = DeliveryPlanConverter.convertToRuleModel(ruleEntity);
            planModel.setDeliveryRuleModel(ruleModel);
            planModels.add(planModel);
        });
        return planModels;
    }
}
